%==========================================================================
%=  This file is part of the Sound Restoration Project
%=  (c) Copyright Industrial Mathematics Institute
%=                University of South Carolina, Department of Mathematics
%=  ALL RIGHTS RESERVED
%=
%=  Author: Borislav Karaivanov
%==========================================================================

%==========================================================================
% List of the files on which this procedure depends:
%
% createMissingDirs.m 
% generateOutputDirectory.m 
% getFileSequenceFromRepresentative.m 
% getImagesFromImages.m 
% getImagesFromMovie.m 
% mergeRangeIntoSeparatedRanges.m 
% patientlyKeepTryingToReadUntilErrorFree.m 
% readAndCheckVideoFrames.m 
% readImageFile.m 
% readImagesByIndexRange.m 
% readdpx.m 
% secureReadableImages.m 
% setUpProjectDirectories.m 
% Image processing toolbox
% ffmpeg
%
%==========================================================================

%==========================================================================
% The function "setUpProjectWithFfmpegReadableMovieOrImage" takes a string
% specifying an ffmpeg-readable image file and sets up a project structure
% with various information relevant to optical sound extraction.
% INPUT: "projectStruct" is a structure whose fields store various
% information relevant to optical sound extraction.
% OUTPUT: "projectStruct" returns a structure whose fields store various
% information relevant to optical sound extraction from the given type of
% files.
%==========================================================================
function projectStruct = setUpProjectWithFfmpegReadableMovieOrImage(projectStruct)

% Get video file information from ffmpeg
[lenVid,fpsVid] = ffmpeg_info(projectStruct.inFile.name);
% If we cannot find correct information, then we assume ffmpeg doesn't
% support it.
if ( (exist('lenVid','var')~=1) || (exist('fpsVid','var')~=1) )
    projectStruct.inFile.kind = 'unknown';
    return;
end

% Get the duration in seconds.
projectStruct.duration = lenVid;

% If duration is less than 0.06 seconds, then we assume that the input is
% an image file.
if (projectStruct.duration < 0.06)
    %======================================================================
    % If this line is reached, we assume that the given file is an image
    % not readable by Matlab but readable by ffmpeg.
    %======================================================================
    projectStruct.inFile.kind = 'imageNonMatlab';
    
    % Start a progress bar as the following may take a while.
    waitbarHandle = waitbar(0.1, 'Reading frames...');
    % Find the maximal run of image files with consecutive indices that
    % match and include the specified template.
    [projectStruct.inFile.dirPath, projectStruct.inFile.namePrefixNoPath, ...
        projectStruct.inFile.indFieldLength, projectStruct.inFile.fileExt, ...
        projectStruct.inFile.frameRangeArr] = ...
        getFileSequenceFromRepresentative(projectStruct.inFile.name);
    % Close the progress bar.
    close(waitbarHandle);
else
    %======================================================================
    % If this line is reached, we assume that the given file is a movie not
    % readable by Matlab but readable by ffmpeg.
    %======================================================================
    projectStruct.inFile.kind = 'videoNonMatlab';
    
    % Get the frame rate.
    projectStruct.frameRate = fpsVid;
    
    % Compute the number of frames.
    numFrames = round(projectStruct.duration*projectStruct.frameRate);
    % Set the potential range of frames that could become available (only
    % after being stored as image files on the disk).
    projectStruct.inFile.frameRangeArr = [1, numFrames];
    
    % Find the prefix (excluding the directory path) of the file name of
    % the specified template.
    [projectStruct.inFile.dirPath, projectStruct.inFile.namePrefixNoPath] = ...
        fileparts(projectStruct.inFile.name);
    projectStruct.inFile.dirPath = [projectStruct.inFile.dirPath filesep];
end

% Set and create the output and scratch directories. Also set a project
% file name and file name prefix for converted image files if any are to be
% generated later on.
projectStruct = setUpProjectDirectories(projectStruct);

% Create a new image file corresponding to the first frame of a movie file
% or the first image in a given sequence of image files.
projectStruct = secureReadableImages(projectStruct, [projectStruct.inFile.frameRangeArr(1), ...
    projectStruct.inFile.frameRangeArr(1)], projectStruct.workFile.fileExt);

% Construct the name of the image file where the first frame will be
% stored. This is necessary in order to set up more of the subfields of the
% project structure's field "work".
tempImageFileName = [projectStruct.workFile.namePrefix, ...
    num2str(projectStruct.inFile.frameRangeArr(1), ...
    ['%0' num2str(projectStruct.workFile.indFieldLength) 'd']), ...
    '.' projectStruct.workFile.fileExt];

% Try to get image information from the new image file for the sake of
% checking if it is Matlab-readable.
imfinfo(tempImageFileName);

% Try to read the new image file as a Matlab-readable image file.
[imageArr, bitDepth] = readImageFile(tempImageFileName, projectStruct.workFile.fileExt);

%==========================================================================
% If this line is reached, we assume that the read file is either a .dpx
% image, or an image in a Matlab-readable format.
%==========================================================================
% Classify the new image file as a Matlab-readable or a .dpx image.
projectStruct.workFile.kind = 'imageMatlabOrDpx';

% Set the dimensions of each frame.
sizeArr = size(imageArr);
projectStruct.height = sizeArr(1);
projectStruct.width = sizeArr(2);
if (numel(sizeArr) == 2)
    projectStruct.numChannels = 1;
else
    projectStruct.numChannels = sizeArr(3);
end
% Set the bit depth per channel.
projectStruct.workFile.bitDepth = bitDepth;

% Set a handle to the reading function.
projectStruct.readFramesFunctHandle = @(indRange, doConvertToGray) ...
    readImagesByIndexRange(indRange, projectStruct.workFile.namePrefix, ...
    projectStruct.workFile.indFieldLength, projectStruct.workFile.fileExt, ...
    doConvertToGray);

% Set a handle to a safer reading function (exactly the same function in
% the case at hand but different when the input is a video file).
projectStruct.readFramesSafelyFunctHandle = @(indRange, doConvertToGray) ...
    readImagesByIndexRange(indRange, projectStruct.workFile.namePrefix, ...
    projectStruct.workFile.indFieldLength, projectStruct.workFile.fileExt, ...
    doConvertToGray);

%==========================================================================
%==========================================================================
%         % The function "secureReadableImages" is tested here but it is
%         % to be used right after the "Extract" button is pressed but before the
%         % actual extraction starts. It is to be called with whatever frame
%         % range is then entered at the GUI boxes.
%         projectStruct = secureReadableImages(projectStruct, [4, 5]);
%         projectStruct.workFile.frameRangeArr
%         projectStruct = secureReadableImages(projectStruct, [8, 10]);
%         projectStruct.workFile.frameRangeArr
%         projectStruct = secureReadableImages(projectStruct, [25, 30]);
%         projectStruct.workFile.frameRangeArr
%         projectStruct = secureReadableImages(projectStruct, [15, 20]);
%         projectStruct.workFile.frameRangeArr
%         projectStruct = secureReadableImages(projectStruct, [21, 24]);
%         projectStruct.workFile.frameRangeArr
%         projectStruct = secureReadableImages(projectStruct, [0, 6]);
%         projectStruct.workFile.frameRangeArr
%         projectStruct = secureReadableImages(projectStruct, [0, 30]);
%         projectStruct.workFile.frameRangeArr
% projectStruct = secureReadableImages(projectStruct, [0, 51]);
% projectStruct.workFile.frameRangeArr

return;
% end of the function "setUpProjectWithFfmpegReadableMovieOrImage"
